package com.zhang.sdk.utils.device;

import android.annotation.SuppressLint;
import android.content.Context;
import android.location.Criteria;
import android.location.GpsSatellite;
import android.location.GpsStatus;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.os.Bundle;
import android.util.Log;

import androidx.annotation.NonNull;

import com.zhang.sdk.SDKApplication;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;

/**
 * gps定位管理类
 * @author zhang.x
 * @date 2020/04/08
 */
@SuppressLint("MissingPermission")
public class GPSLocationManager {
    private static final String TAG = "GPSLocationManager";
    @SuppressLint("StaticFieldLeak")
    private volatile static GPSLocationManager sxLocationManager;
    private LocationManager manager;
    private Context context;
    private String providerName;
    private boolean isInit = false;
    private HashSet<LocationCallBack> locationListeners;
    private GPSLocationManager(){
        init(SDKApplication.appContext);
    }
    public static GPSLocationManager getInstance() {
        if (sxLocationManager == null) {
            synchronized (GPSLocationManager.class) {
                if (sxLocationManager == null) {
                    sxLocationManager = new GPSLocationManager();
                }
            }
        }
        return sxLocationManager;
    }

    /**
     * 直接获取当前位置
     * @return 位置信息
     */
    public LatLng getCurrentPosition() {
        //判断GPS是否正常启动
        if (manager == null || !manager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
            //gps没有正常启动
            return LatLng.newErrorInstance();
        }
        Location location = manager.getLastKnownLocation(providerName);
        return location2LatLng(location);
    }

    /**
     * 初始化定位管理
     */
    public void init(Context context) {
        if (isInit) {
            return;
        }
        this.context = context;
        manager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
        //判断GPS是否正常启动
        if (manager == null || !manager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
            //gps没有正常启动
            return;
        }
//        LogUtil.e("初始化定位");
        if (locationListeners == null) {
            locationListeners = new HashSet<>();
        }
        // 为获取地理位置信息时设置查询条件，目前查询结束后为gps
        providerName = manager.getBestProvider(getCriteria(), true);
        //添加卫星状态改变监听
        manager.addGpsStatusListener(gpsStatusListener);
        //1000位最小的时间间隔，1为最小位移变化；也就是说每隔1000ms会回调一次位置信息
        manager.requestLocationUpdates(providerName, 8000, 0, locationListener);
        isInit = true;
    }

    /**
     * 销毁定位管理
     */
    public void destroy() {
        if (isInit) {
            if (manager != null) {
                manager.removeUpdates(locationListener);
                manager.removeGpsStatusListener(gpsStatusListener);
            }
            locationListeners.clear();
            locationListeners = null;
            isInit = false;
        }
    }

    private GpsStatus.Listener gpsStatusListener = new GpsStatus.Listener() {
        @Override
        public void onGpsStatusChanged(int event) {
            //卫星状态改变
            if (event == GpsStatus.GPS_EVENT_SATELLITE_STATUS) {
                //获取当前状态
                GpsStatus gpsStatus = manager.getGpsStatus(null);
                //获取卫星颗数的默认最大值
                int maxSatellites = gpsStatus.getMaxSatellites();
                //获取所有的卫星
                Iterator<GpsSatellite> iters = gpsStatus.getSatellites().iterator();
                //卫星颗数统计
                int count = 0;
                List<GpsSatellite> list = new ArrayList<>();
                StringBuilder sb = new StringBuilder();

                while (iters.hasNext() && count <= maxSatellites) {
                    GpsSatellite s = iters.next();
                    //卫星的信噪比
                    float snr = s.getSnr();
                    if (snr != 0) {
                        count++;
                        list.add(s);
                        sb.append("第").append(count).append("颗").append("：").append(snr);
                    }
                }
            }
        }
    };

    private void callListeners(Location location) {
        if (locationListeners == null || locationListeners.isEmpty()) {
            return;
        }
        LatLng latLng = location2LatLng(location);
        for (LocationCallBack callBack : locationListeners) {
            callBack.onGetLocation(latLng);
        }
    }

    private LocationListener locationListener = new LocationListener() {
        /**
         * 位置信息变化时触发
         */
        @Override
        public void onLocationChanged(Location location) {
            callListeners(location);
        }

        /**
         * GPS状态变化时触发
         */
        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {
            switch (status) {
                // GPS状态为可见时
                case LocationProvider.AVAILABLE:
                    Log.i("TAG", "当前GPS状态为可见状态");
                    break;
                // GPS状态为服务区外时
                case LocationProvider.OUT_OF_SERVICE:
                    Log.i("TAG", "当前GPS状态为服务区外状态");
                    break;
                // GPS状态为暂停服务时
                case LocationProvider.TEMPORARILY_UNAVAILABLE:
                    Log.i("TAG", "当前GPS状态为暂停服务状态");
                    break;
            }
        }

        /**
         * GPS开启时触发
         */
        @Override
        public void onProviderEnabled(String provider) {
            Location location = manager.getLastKnownLocation(provider);
            callListeners(location);
        }

        /**
         * GPS禁用时触发
         */
        @Override
        public void onProviderDisabled(String provider) {
//            callListeners(null);
        }
    };

    /**
     * 为获取地理位置信息时设置查询条件，目前测试加上查询条件后便固定为{@link LocationManager#GPS_PROVIDER}
     */
    private Criteria getCriteria() {
        Criteria criteria = new Criteria();
        // 设置定位精确度 Criteria.ACCURACY_COARSE比较粗略，Criteria.ACCURACY_FINE则比较精细
        criteria.setAccuracy(Criteria.ACCURACY_FINE);
        // 设置是否要求速度
        criteria.setSpeedRequired(false);
        // 设置是否允许运营商收费
        criteria.setCostAllowed(false);
        // 设置是否需要方位信息
        criteria.setBearingRequired(true);
        // 设置是否需要海拔信息
        criteria.setAltitudeRequired(true);
        // 设置对电源的需求
        criteria.setPowerRequirement(Criteria.POWER_HIGH);
        return criteria;
    }

    /**
     * 根据真实经纬度判断在不在中国境内，采用方形判断
     * @param lat
     * @param lon
     * @return
     */
    private boolean outOfChina(double lat, double lon) {
        return lon < 72.004 || lon > 137.8347 || lat < 0.8293 || lat > 55.8271;
    }

    /**
     * 84 to 火星坐标系 (GCJ-02) World Geodetic System ==> Mars Geodetic System
     *
     * @param lat 纬度
     * @param lon 经度
     * @return
     */
    private static double pi = 3.1415926535897932384626;
    private static double a = 6378245.0;
    private static double ee = 0.00669342162296594323;
    LatLng gps84_To_Gcj02(double lat, double lon) {
        try {
            if (outOfChina(lat, lon)) {
                return null;
            }
            double dLat = transformLat(lon - 105.0, lat - 35.0);
            double dLon = transformLon(lon - 105.0, lat - 35.0);
            double radLat = lat / 180.0 * pi;
            double magic = Math.sin(radLat);
            magic = 1 - ee * magic * magic;
            double sqrtMagic = Math.sqrt(magic);
            dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
            dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
            double mgLat = lat + dLat;
            double mgLon = lon + dLon;
            return new LatLng(mgLat, mgLon);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private double transformLat(double x, double y) {
        double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
        ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(y * pi) + 40.0 * Math.sin(y / 3.0 * pi)) * 2.0 / 3.0;
        ret += (160.0 * Math.sin(y / 12.0 * pi) + 320 * Math.sin(y * pi / 30.0)) * 2.0 / 3.0;
        return ret;
    }

    private double transformLon(double x, double y) {
        double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
        ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(x * pi) + 40.0 * Math.sin(x / 3.0 * pi)) * 2.0 / 3.0;
        ret += (150.0 * Math.sin(x / 12.0 * pi) + 300.0 * Math.sin(x / 30.0 * pi)) * 2.0 / 3.0;
        return ret;
    }

    public static class LatLng {
        //纬度
        public double latitude;
        //经度
        public double longitude;

        /** 定位失败实体类 */
        public static LatLng newErrorInstance() {
            return new LatLng(-1, -1);
        }

        public LatLng(double latitude, double longitude) {
            this.latitude = latitude;
            this.longitude = longitude;
        }

        @NonNull
        @Override
        public String toString() {
            return longitude + ", " + latitude;
        }
    }

    public synchronized void setLocationCallBack(LocationCallBack listener) {
        if (locationListeners == null) {
            locationListeners = new HashSet<>();
        }
        locationListeners.add(listener);
    }

    public synchronized void removeLocationCallBack(LocationCallBack listener) {
        try {
            if (listener == null || locationListeners == null || locationListeners.isEmpty()) {
                return;
            }
            locationListeners.remove(listener);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public interface LocationCallBack {
        void onGetLocation(LatLng latLng);
    }

    private LatLng location2LatLng(Location location) {
        if (location != null) {
            double latitude = location.getLatitude();
            double longitude = location.getLongitude();
            if (!outOfChina(latitude, longitude)) {
                //LatLng desLatLng = gps84_To_Kld(latitude, longitude, location.getAltitude());
                return gps84_To_Gcj02(latitude, longitude);
            } else {
                Log.e(TAG, "不支持在中国境外定位！！");
            }
        }
        return LatLng.newErrorInstance();
    }
}
